package io.adrainty.bolee.security.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.adrainty.bolee.framework.commons.basic.ResponseCode;
import io.adrainty.bolee.framework.commons.basic.TreeDTO;
import io.adrainty.bolee.framework.commons.basic.TreeItemDTO;
import io.adrainty.bolee.framework.commons.exception.AppException;
import io.adrainty.bolee.framework.commons.utils.IdentifierNoUtil;
import io.adrainty.bolee.framework.commons.utils.TreeItemUtil;
import io.adrainty.bolee.security.models.constants.ResourceConstants;
import io.adrainty.bolee.security.models.entity.MenuEntity;
import io.adrainty.bolee.security.models.entity.MenuMetaEntity;
import io.adrainty.bolee.security.models.entity.ResourceEntity;
import io.adrainty.bolee.security.models.entity.RoleEntity;
import io.adrainty.bolee.security.repository.IResourceRepository;
import io.adrainty.bolee.security.repository.IRoleRepository;
import io.adrainty.bolee.security.service.IResourceService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author AdRainty
 * @version V1.0.0
 * @since 2025/8/9 下午11:32
 */

@Service
public class ResourceServiceImpl implements IResourceService {

    @Resource
    private IResourceRepository resourceRepository;

    @Resource
    private IRoleRepository roleRepository;

    @Override
    @Cacheable(value = ResourceConstants.LIST, key = "#userId")
    public List<ResourceEntity> findResourceByUserId(Long userId) {
        return resourceRepository.findResourceByUserId(userId);
    }

    @Override
    @Cacheable(value = ResourceConstants.MENUS, key = "#systemCode")
    public List<MenuEntity> getMenus(String systemCode) {
        List<ResourceEntity> resourceEntityList = resourceRepository.getMenuResourceList();
        ResourceEntity parentResource = resourceEntityList.stream()
                .filter(resourceEntity ->
                        IdentifierNoUtil.ROOT_PARENT_ID.equals(resourceEntity.getParentResourceNo()) &&
                                ResourceConstants.SYSTEM.equals(resourceEntity.getResourceType()))
                .findFirst()
                .orElse(null);
        if (parentResource == null) return List.of();

        List<String> resourceNoList = resourceEntityList.stream().map(ResourceEntity::getResourceNo).toList();
        Map<String, List<RoleEntity>> roleEntityMap = roleRepository.findRoleListByResourceNo(resourceNoList);

        return recursionMenuEntity(new ArrayList<>(), parentResource, roleEntityMap,
                resourceEntityList, ResourceConstants.COMPONENT_LAYOUT);
    }

    @Override
    public TreeDTO getResourceTree(ResourceEntity resourceEntity) {
        String parentDeptNo = StringUtils.defaultIfBlank(resourceEntity.getParentResourceNo(), IdentifierNoUtil.ROOT_PARENT_ID);
        List<ResourceEntity> resourceEntities = resourceRepository.findSubResourceList(parentDeptNo);
        if (CollUtil.isEmpty(resourceEntities)) {
            throw new AppException(ResponseCode.DEPT_NOT_FOUND);
        }

        List<String> checkedIds = resourceEntity.getCheckedResourceNos() == null? List.of() :
                Arrays.stream(resourceEntity.getCheckedResourceNos()).toList();
        List<String> expandedIds = new ArrayList<>();
        // 下面Contains要用 转为Set加快效率
        Set<String> checkedIdSet = new HashSet<>(checkedIds);
        List<TreeItemDTO> treeItemDTOList = resourceEntities.stream().map(resource -> {
            TreeItemDTO treeItemDTO = TreeItemDTO.builder()
                    .id(resource.getResourceNo())
                    .parentId(resource.getParentResourceNo())
                    .label(resource.getResourceName())
                    .isChecked(checkedIdSet.contains(resource.getResourceNo()))
                    .build();
            // 展开项, 默认展开为二级
            // 如果为100_001_001_000_000 > 100_001_001 才展开
            // 如果为100_001_001_001_000 > 100_001_001_001 就不展开
            if (IdentifierNoUtil.processNo(resource.getResourceNo()).length() / 3 == 3) {
                expandedIds.add(resource.getResourceNo());
            }
            return treeItemDTO;
        }).toList();

        return TreeItemUtil.returnTreeDTO(checkedIds, expandedIds, parentDeptNo, treeItemDTOList);
    }

    @Override
    public IPage<ResourceEntity> getResourcePage(Integer pageNum, Integer pageSize, ResourceEntity resourceEntity) {
        return resourceRepository.findResourcePage(resourceEntity, pageNum, pageSize);
    }

    @Override
    public ResourceEntity createResource(ResourceEntity resourceEntity) {
        if (StringUtils.isBlank(resourceEntity.getParentResourceNo())) {
            resourceEntity.setParentResourceNo(IdentifierNoUtil.ROOT_PARENT_ID);
        }

        String currentResourceNo = createResourceNo(resourceEntity.getParentResourceNo());
        resourceEntity.setResourceNo(currentResourceNo);
        boolean result = resourceRepository.createResource(resourceEntity);
        if (!result) {
            throw new AppException(ResponseCode.DEPT_CREATE_ERROR);
        }
        return resourceEntity;
    }

    @Override
    public Boolean updateResource(ResourceEntity resourceEntity) {
        if (resourceEntity.getId() == null) {
            throw new AppException(ResponseCode.ILLEGAL_PARAMETER);
        }
        return resourceRepository.updateResource(resourceEntity);
    }

    @Override
    public List<ResourceEntity> findResourceList(ResourceEntity resourceEntity) {
        return resourceRepository.listResource(resourceEntity);
    }

    /**
     * 创建资源编号
     *
     * @param parentResourceNo 父级资源编号
     * @return 资源编号
     */
    private String createResourceNo(String parentResourceNo) {
        List<ResourceEntity> resourceEntities = resourceRepository.findSubLevel1ResourceList(parentResourceNo);
        if (CollUtil.isEmpty(resourceEntities)) {
            return IdentifierNoUtil.createNo(parentResourceNo, false);
        } else {
            return resourceEntities.stream()
                    .map(dept -> Long.valueOf(dept.getResourceNo()))
                    .max(Comparator.comparing(i -> i))
                    .map(String::valueOf)
                    .map(i -> IdentifierNoUtil.createNo(i, true))
                    .orElse(IdentifierNoUtil.createNo(parentResourceNo, false));
        }
    }

    /**
     * 递归菜单实体
     *
     * @param result           递归结果
     * @param parentResource 父级资源
     * @param roleEntityMap    角色资源关系
     * @param resourceEntityList 资源列表
     * @param componentLayout  组件布局
     * @return 菜单实体
     */
    private List<MenuEntity> recursionMenuEntity(List<MenuEntity> result, ResourceEntity parentResource,
                                                 Map<String, List<RoleEntity>> roleEntityMap, List<ResourceEntity> resourceEntityList, String componentLayout) {
        if (parentResource == null) return result;
        List<ResourceEntity> childrenList = resourceEntityList.stream()
                .filter(resourceEntity -> parentResource.getResourceNo().equals(resourceEntity.getParentResourceNo()))
                .toList();

        for (ResourceEntity resourceEntity : childrenList) {
            if (!parentResource.getResourceNo().equals(resourceEntity.getParentResourceNo())) continue;
            List<String> roleList = roleEntityMap.getOrDefault(resourceEntity.getResourceNo(), new ArrayList<>()).stream()
                    .map(RoleEntity::getLabel).toList();

            MenuMetaEntity meta = MenuMetaEntity.builder()
                    .icon(resourceEntity.getIcon())
                    .title(resourceEntity.getResourceName())
                    .roles(roleList)
                    .build();
            MenuEntity menu = MenuEntity.builder()
                    .id(resourceEntity.getId())
                    .name(resourceEntity.getResourceName())
                    .hidden(Boolean.FALSE)
                    .component(resourceEntity.getRequestPath())
                    .meta(meta)
                    .build();
            if (ResourceConstants.COMPONENT_LAYOUT.equals(componentLayout)) {
                menu.setPath("/" + resourceEntity.getRequestPath());
                menu.setComponent(componentLayout);
            } else {
                menu.setPath(resourceEntity.getRequestPath());
                menu.setComponent(componentLayout + StrPool.SLASH + resourceEntity.getRequestPath());
            }

            List<MenuEntity> currentChildren = this.recursionMenuEntity(
                    new ArrayList<>(), resourceEntity, roleEntityMap, resourceEntityList, resourceEntity.getRequestPath());
            if (CollUtil.isNotEmpty(currentChildren)) {
                menu.setRedirect(StrPool.SLASH + resourceEntity.getResourceName() + StrPool.SLASH + currentChildren.get(0).getName());
            }
            menu.setChildren(currentChildren);
            result.add(menu);
        }

        return result;
    }
}
